package org.tang.gateway.config;

import com.alibaba.nacos.client.naming.event.InstancesChangeEvent;
import com.alibaba.nacos.common.notify.Event;
import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.common.notify.listener.Subscriber;
import com.google.common.collect.Sets;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ZoneAwareLoadBalancer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.net.URI;
import java.util.Objects;
import java.util.Set;

/**
 * @author Tang
 * @classname InstancesChangeSubscriber
 * @description [ 订阅实例变化事件 ]
 * @date 2022/9/25 20:49
 */
@Slf4j
public class InstancesChangeSubscriber extends Subscriber<InstancesChangeEvent> implements EnvironmentAware {

    /**
     * ribbon 的 client Factory
     */
    @Resource
    private SpringClientFactory springClientFactory;

    /**
     * 网关的配置加载器（多级加载器）
     */
    private final RouteDefinitionLocator routeDefinitionLocator;

    /**
     * 可以主动触发的 ribbon client 实例
     */
    private final Set<String> clientNames;

    private final RibbonWatchClients ribbonWatchClients;

    private String group;

    public InstancesChangeSubscriber(RouteDefinitionLocator routeDefinitionLocator,RibbonWatchClients ribbonWatchClients) {
        // 注册事件通知
        NotifyCenter.registerSubscriber(this);
        this.clientNames = Sets.newHashSet();
        this.routeDefinitionLocator = routeDefinitionLocator;
        this.ribbonWatchClients = ribbonWatchClients;
    }

    @Override
    public void onEvent(InstancesChangeEvent event) {
        if (!CollectionUtils.isEmpty(this.clientNames)) {
            String clientName = this.clientNames.stream()
                    .filter(name -> event.getServiceName().equals(group + name))
                    .findFirst()
                    .orElse("");
            if (!StringUtils.isEmpty(clientName)) {
                // 获取指定ribbon client 的 ILoadBalancer
                ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(clientName);
                // 转一下 ZoneAwareLoadBalancer 是默认注入的
                if (Objects.nonNull(loadBalancer) && loadBalancer instanceof ZoneAwareLoadBalancer) {
                    ZoneAwareLoadBalancer<Server> zoneAwareLoadBalancer = (ZoneAwareLoadBalancer<Server>) loadBalancer;
                    // 手动更新服务列表
                    zoneAwareLoadBalancer.updateListOfServers();
                    log.info("更新服务列表：" + loadBalancer);
                }
            }
            log.info("接收到事件：ServiceName:" + event.getServiceName() + ",GroupName:" + event.getGroupName());
        }
    }

    /**
     * InstancesChangeEvent 实例改变时的触发事件，nacos 无法区分是上线和下线事件
     * @return
     */
    @Override
    public Class<? extends Event> subscribeType() {
        return InstancesChangeEvent.class;
    }

    @Override
    public void setEnvironment(Environment environment) {
        // 获取配置的group
        this.group = getGroupProperty(environment) + "@@";
        // 是否初始化所有的
        if (ribbonWatchClients.isAllClient()) {
            // 获取网关配置所有的Route实例
            routeDefinitionLocator.getRouteDefinitions()
                    .map(RouteDefinition::getUri)
                    .map(URI::toString)
                    .filter(uri -> uri.startsWith("lb"))
                    .map(uri -> uri.substring(5))
                    .subscribe(clientNames::add);
        }else {
            this.clientNames.addAll(ribbonWatchClients.getIncludeClients());
        }
        this.clientNames.removeAll(this.ribbonWatchClients.getExcludeClients());
    }

    private String getGroupProperty(Environment environment) {
        // 从 Environment 中 get
        return environment.getProperty("spring.cloud.nacos.discovery.group", "DEFAULT_GROUP");
    }

}
